package com.jacky.media;

import android.annotation.SuppressLint;
import android.content.Context;
import android.hardware.display.VirtualDisplay;
import android.media.AudioManager;
import android.media.CamcorderProfile;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaCodecList;
import android.media.MediaRecorder;
import android.os.Build;
import android.util.DisplayMetrics;

import androidx.annotation.RequiresApi;

import com.jacky.log.Logger;
import com.meituan.flutter_logan.*;

import java.io.IOException;


@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class MediaRecorderProxy {

    /* 与 @{com.enotary.mobile.screen.FaXinServiceConnection}类的值保持一致 */
    private static final int ERR_DURATION = 3;
    private static final int ERR_MAX_SIZE = 4;
    private static final int ERR_STOP_AUTO = 5;
    private static final int ERR_UNKOWN = 6;
    private static final int SCREEN_Err = 11;
    private static final int LOW_MEMORY = 12;

    private int WIDTH = 0;
    private int HEIGHT = 0;

    private boolean userCodecScreen = false;
    private String filePath;
    private int maxRecordSecondTime;
    private MediaRecorder mediaRecorder;
    private ScreenRecorder screenRecorder;
    private MediaRecorder.OnErrorListener onErrorListener;

    public MediaRecorderProxy(int w, int h, String filePath, int recordTime) {
        this.filePath = filePath;
        this.maxRecordSecondTime = recordTime;
        this.WIDTH = w;
        this.HEIGHT = h;
    }

    public MediaRecorderProxy setOnErrorListener(MediaRecorder.OnErrorListener listener) {
        this.onErrorListener = listener;
        return this;
    }

    private boolean userCodeScreen() {
//        String model = Build.MODEL;
//        if("TAS-AL00".equals(model)) return true; //华为 mate30
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.R;
    }

    public void prepare(boolean isMicOpen, VirtualDisplay display) throws IOException  {
        boolean screen_default = false;
        userCodecScreen = false;
        int videoBitRate, videoFrameRate;
        if(screen_default) {
            CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_720P);
            videoFrameRate = profile.videoFrameRate;
            videoBitRate = profile.videoBitRate;
        } else {
            int r = userCodeScreen() ? 1 : 5;
            int b = WIDTH * HEIGHT * r;
            videoFrameRate = 20;
            videoBitRate = b;
        }
        Logger.w(videoBitRate, videoFrameRate, WIDTH, HEIGHT);
        if(userCodecScreen) prepareCloudPhone(isMicOpen, display, videoBitRate, videoFrameRate);
        else prepareMediaRecord(isMicOpen, display, videoBitRate, videoFrameRate);
    }

    /**
     * 正常手机的录屏方式
     * @param isMicOpen
     * @throws IOException
     */
    @SuppressLint("WrongConstant")
    private void prepareMediaRecord(boolean isMicOpen, VirtualDisplay display,int videoBitRate, int videoFrameRate) throws IOException {
        boolean isSystem = false;
        boolean isHem = false;
        mediaRecorder = new MediaRecorder();
        if(isMicOpen) {
            mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        } else if(isSystem || isHem) {
            mediaRecorder.setAudioSource(MediaRecorder.AudioSource.REMOTE_SUBMIX);
        }
        mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
        mediaRecorder.setMaxDuration(maxRecordSecondTime * 1000);
        mediaRecorder.setOutputFile(filePath);
        mediaRecorder.setVideoSize(WIDTH, HEIGHT);

        mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
        if(isMicOpen || isSystem || isHem) {
            mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
        }
        mediaRecorder.setVideoEncodingBitRate(videoBitRate);
        mediaRecorder.setVideoFrameRate(videoFrameRate);
        mediaRecorder.prepare();
        display.setSurface(mediaRecorder.getSurface());

        String ss = "width:" + WIDTH + ",height:" + HEIGHT + ",rate:" + videoBitRate + ",frame:" + videoFrameRate + ",time:"+maxRecordSecondTime;
        FlutterLoganPlugin.w(ss, 2);

        mediaRecorder.setOnInfoListener((MediaRecorder mr, int what, int extra) -> {
            String s = "MediaRecorder Info Listener " + what + extra;
            Logger.i(s);
            FlutterLoganPlugin.w(s, 1);
            int i;
            switch (what) {
                case MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED: i = ERR_DURATION; break;
                case MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED: i = ERR_MAX_SIZE; break;
                case MediaRecorder.MEDIA_RECORDER_INFO_UNKNOWN: i = ERR_UNKOWN; break;
                default: return;
            }
            recordFail(i);
        });
        mediaRecorder.setOnErrorListener((MediaRecorder mr, int what, int extra) -> {
            String s = "MediaRecorder Err Listener " + what + extra;
            Logger.i(s);
            FlutterLoganPlugin.w(s, 1);
            if(what == MediaRecorder.MEDIA_ERROR_SERVER_DIED) recordFail(ERR_STOP_AUTO);
            else recordFail(ERR_UNKOWN);
        });
    }

    private void prepareCloudPhone(boolean isMicOpen, VirtualDisplay display, int videoBitRate, int videoFrameRate) {
        MediaCodecList mediaCodecList = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
        MediaCodecInfo[] list = mediaCodecList.getCodecInfos();
//        for(MediaCodecInfo info : list) {
//            if(!info.isEncoder()) continue;
//            Logger.i(info.getName(), info.getSupportedTypes());
//        }
        VideoEncodeConfig video = new VideoEncodeConfig(WIDTH, HEIGHT, videoBitRate,
                videoFrameRate, 15, null, "video/avc",
                null);

        String vcodecName = getCodeName(list, "video/avc", "OMX.google.h264.encoder", video);
        String acodecName = getCodeName(list, "audio/mp4a-latm", "OMX.google.aac.encoder", null);
        video.codecName = vcodecName;

        AudioEncodeConfig audio = isMicOpen ? new AudioEncodeConfig(acodecName, "audio/mp4a-latm",
                80_000, 44100, 1, 1) : null;
        screenRecorder = new ScreenRecorder(video, audio, display, filePath);
        screenRecorder.setCallback(new ScreenRecorder.Callback() {
            @Override
            public void onStop(Throwable error) { //IllegalArgumentException
                if(error != null) {
                    FlutterLoganPlugin.w(Logger.getStackTraceString(error), 2);
                    recordFail(error instanceof MediaCodec.CodecException ? LOW_MEMORY : SCREEN_Err);
                }
            }

            @Override
            public void onStart() {}

            @Override
            public void onRecording(long presentationTimeUs) {}
        });
    }

    private String getCodeName(MediaCodecInfo[] mediaCodecList, String mimeType, String defaultCodecName, VideoEncodeConfig video) {
        for(MediaCodecInfo info : mediaCodecList) {
            if(!info.isEncoder()) continue;
            for(String type : info.getSupportedTypes()) {
                if(type.equalsIgnoreCase(mimeType)) {
                    if(video == null) return info.getName();
                    boolean b = video.checkMediaCodecName(info.getName());
                    Logger.i(info.getName(), info.getSupportedTypes(), b);
                    if(b) return info.getName();
                }
            }
        }
        return defaultCodecName;
    }

    public void start() {
        if(userCodecScreen) screenRecorder.start();
        else mediaRecorder.start();
    }

    public void stop() {
        if(userCodecScreen) {
            screenRecorder.quit();
            try {
                Thread.sleep(800);//等待视频落地
            } catch (InterruptedException ignored) {
            }
        } else {
            try {
                mediaRecorder.setOnErrorListener(null);
                mediaRecorder.setOnInfoListener(null);
                mediaRecorder.stop();
                mediaRecorder.reset();
            } catch (Exception e) {
                Logger.w("ScreenRecordService stop fail...");
            }
            mediaRecorder.release();
        }
    }

    private void recordFail(int code) {
        if(onErrorListener != null) onErrorListener.onError(null, code, 0);
    }

    /**
     * 录屏结束，音量重置
     */
    public static void resetVolume(Context context) {
        /*
         *  录屏结束的时候，
         *  如果是普通手机则音量改成最大音量的1/3
         */
        AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        int max = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
        int m =  (max / 3);
        am.setStreamVolume(AudioManager.STREAM_MUSIC, m, AudioManager.FLAG_SHOW_UI);
    }

    /**
     *  录屏开始，设置音量大小
     */
    public static void changeVolume(Context context) {
        /*
         *  录屏开始的时候，
         *  如果是普通手机则音量改成最大音量的1/2
         */
        AudioManager am = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
        int max = am.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
        int m =   (max / 2);
        am.setStreamVolume(AudioManager.STREAM_MUSIC, m, AudioManager.FLAG_SHOW_UI);
    }

    public static int[] calculeResolution(DisplayMetrics metrics) {
        int mDisplayWidth, mDisplayHeight;
        Logger.w("before: ", metrics.widthPixels, metrics.heightPixels);
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
            mDisplayHeight = metrics.heightPixels;
            mDisplayWidth = metrics.widthPixels;
        }else{
            double maxRes = 1280.0;
            if (metrics.scaledDensity >= 3.0f) {
                maxRes = 1920.0;
            }
            if (metrics.widthPixels > metrics.heightPixels) {
                double rate = metrics.widthPixels / maxRes;
                mDisplayWidth = (int) maxRes;
                mDisplayHeight = (int)(metrics.heightPixels / rate);
            } else {
                double rate = metrics.heightPixels / maxRes;
                mDisplayHeight = (int) maxRes;
                mDisplayWidth = (int) (metrics.widthPixels / rate);
            }
        }
        mDisplayWidth += mDisplayWidth % 2;
        mDisplayHeight += mDisplayHeight % 2;
        Logger.w("after: ", mDisplayWidth, mDisplayHeight);
        int[] size = new int[2];
        size[0] = mDisplayWidth;
        size[1] = mDisplayHeight;
        return  size;
    }
}
